package org.orz.story.member.biz.account.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.orz.cloud.auth.AuthConstants;
import org.orz.cloud.auth.ServiceContextUtil;
import org.orz.cloud.auth.UserUtil;
import org.orz.cloud.auth.domain.UserInfo;
import org.orz.cloud.auth.domain.UserToken;
import org.orz.cloud.common.enums.AliveFlagEnum;
import org.orz.cloud.redis.RedisUtil;
import org.orz.story.member.biz.account.enums.AccountStatusEnum;
import org.orz.story.member.biz.account.enums.LoginModeEnum;
import org.orz.story.member.biz.account.model.req.*;
import org.orz.story.member.biz.account.persistence.entities.MemAccount;
import org.orz.story.member.biz.account.persistence.mappers.MemAccountMapper;
import org.orz.story.member.biz.account.service.MemAccountService;
import org.orz.story.member.client.PmsClient;
import org.orz.story.member.common.exception.MemberException;
import org.orz.story.pms.api.enums.WorkbenchCodeEnum;
import org.orz.story.pms.api.model.vo.AccountWorkbenchListVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
public class MemAccountServiceImpl implements MemAccountService {
    @Autowired
    private MemAccountMapper memAccountMapper;
    @Autowired
    private PmsClient pmsClient;
    @Value("${login.mode}")
    private Integer loginMode;
    @Value("${login.max:}")
    private Integer maxDevice;
    /** 七天 */
    private static final Integer SEVEN_DAY_MILLS = 1000 * 60 * 60 * 24 * 7;


    @Override
    public void register(RegisterReq dto) {
        //1.校验账号是否已经存在
        MemAccount memAccount = memAccountMapper.selectOne(new LambdaQueryWrapper<MemAccount>()
                .eq(MemAccount::getUsername, dto.getUsername())
                .eq(MemAccount::getAliveFlag, AliveFlagEnum.ALIVE.getKey()));
        if(memAccount != null){
            throw new MemberException(MemberException.Type.MEM_0004);
        }

        //2.注册
        MemAccount insert = new MemAccount();
        insert.setUsername(dto.getUsername());
        insert.setPassword(dto.getPassword());
        insert.setStatus(AccountStatusEnum.ENABLE.getKey());
        insert.setCreateUser(0L);
        insert.setCreateDate(new Date());
        memAccountMapper.insert(insert);

        //TODO 3.绑定默认权限

    }

    /**
     * 登录需求：
     * 1.根据token可以找到用户的id
     * 2.根据用户的id可以找到用户的信息
     *
     * 单点登录方案
     * userInfo中记录当前用户的token，这样如果开启了单点登录功能，就可以在登录的时候删除之前保存的token，
     * 实现只有一个设备登录的功能。如果是要求三个设备呢? 可以将token做成list
     *
     * 所以要做的：
     * 1.保存用户的token，token中记录用户的id
     * 2.查询缓存中是否存在当前用户的userInfo
     *         如果没有，就保存当前用户的信息
     */
    @Override
    public UserInfo login(LoginReq req, String workbenchCode) {
        // 查询账户是否存在
        MemAccount account = memAccountMapper.selectOne(new LambdaQueryWrapper<MemAccount>()
                .eq(MemAccount::getUsername, req.getUsername())
                .eq(MemAccount::getAliveFlag, AliveFlagEnum.ALIVE.getKey()));
        if(account == null){
            throw new MemberException(MemberException.Type.MEM_0001);
        }

        // 校验是否可登录当前平台
        List<AccountWorkbenchListVo> workbenchList = pmsClient.getAccountWorkbenchList(account.getId());
        checkHasCurrentPlatformLoginAuth(workbenchList, ServiceContextUtil.getWorkbenchWithExistCheck());
        //验证密码是否错误
        if(!account.getPassword().equals(req.getPassword())){
            throw new MemberException(MemberException.Type.MEM_0003);
        }

        //执行登录
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getResponse();
        return doLogin(response, account, workbenchList);
    }

    @Override
    public Page<MemAccount> listAccountInfo(ListAccountInfoReq req) {
        PageHelper.startPage(req.getPageInfo().getPageNum(), req.getPageInfo().getPageSize());
        return (Page<MemAccount>) memAccountMapper.selectList(new LambdaQueryWrapper<MemAccount>()
            .eq(MemAccount::getAliveFlag,AliveFlagEnum.ALIVE.getKey())
            .orderByDesc(MemAccount::getId));
    }

    @Override
    public void updateAccount(UpdateAccountReq req) {
        UserInfo userInfo = UserUtil.getUserInfoWithExistCheck();
        MemAccount account = new MemAccount();
        account.setId(req.getId());
        account.setStatus(req.getStatus());
        account.setCreateUser(userInfo.getAccountId());
        account.setCreateDate(new Date());
        memAccountMapper.updateById(account);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addAccount(AddAccountReq req) {
        // 校验账号是否已经存在
        List<MemAccount> accounts = memAccountMapper.selectList(new LambdaQueryWrapper<MemAccount>()
                .eq(MemAccount::getUsername, req.getUsername())
                .eq(MemAccount::getAliveFlag, AliveFlagEnum.ALIVE.getKey()));
        if(CollUtil.isNotEmpty(accounts)){
            throw new MemberException(MemberException.Type.MEM_0004);
        }

        // 1.创建用户账号
        UserInfo userInfo = UserUtil.getUserInfoWithExistCheck();
        MemAccount memAccount = new MemAccount();
        memAccount.setUsername(req.getUsername());
        memAccount.setPassword(req.getPassword());
        memAccount.setStatus(req.getStatus());
        memAccount.setCreateUser(userInfo.getAccountId());
        memAccount.setCreateDate(new Date());
        memAccountMapper.insert(memAccount);

        // 2.创建权限账号
        pmsClient.createPmsAccount(memAccount.getId(), WorkbenchCodeEnum.PC_ADMIN);
    }

    private UserInfo doLogin(HttpServletResponse response, MemAccount account, List<AccountWorkbenchListVo> workbenchList) {
        String userInfoKey = AuthConstants.REDIS_KEY_USER_INFO + account.getId();
        String userInfoStr = RedisUtil.get(userInfoKey);
        String tokenStr = UUID.randomUUID().toString();

        List<String> tokenList = new ArrayList<>();
        if(StrUtil.isNotBlank(userInfoStr)){
            //清除过期的token
            List<String> cleanToken = cleanToken(JSON.parseObject(userInfoStr, UserInfo.class));
            if(CollUtil.isNotEmpty(cleanToken)){
                //单点登录
                if(LoginModeEnum.SINGLETON_LOGIN.getKey().equals(loginMode)){
                    //删除其他设备登录的token，然后保存当前的token
                    for (String item : cleanToken) {
                        RedisUtil.delete(AuthConstants.REDIS_KEY_USER_TOKEN + item);
                    }
                    //最大设备登录
                } else if(LoginModeEnum.MAX_DEVICE_LIMIT.getKey().equals(loginMode)) {
                    //如果超出数量，就提示超出数量了
                    if(maxDevice <= cleanToken.size()){
                        throw new MemberException(MemberException.Type.MEM_0002);
                    }
                    //没有超出数量，就将当前token加入到tokenList中
                    tokenList.addAll(cleanToken);
                }
            }
        }
        tokenList.add(tokenStr);

        //保存userToken
        String userTokenKey = AuthConstants.REDIS_KEY_USER_TOKEN + tokenStr;
        UserToken token = new UserToken();
        token.setAccountId(account.getId());
        RedisUtil.set(userTokenKey, JSON.toJSONString(token), SEVEN_DAY_MILLS);

        //保存userInfo
        UserInfo userInfo = new UserInfo();
        userInfo.setAccountId(account.getId());
        userInfo.setTokenList(tokenList);

        //查询用户所属的工作台
        List<String> workbenchCodeList = workbenchList.stream().map(AccountWorkbenchListVo::getWorkbenchCode).distinct().collect(Collectors.toList());
        userInfo.setWorkBenchList(workbenchCodeList);

        RedisUtil.set(userInfoKey, JSON.toJSONString(userInfo), SEVEN_DAY_MILLS);
        response.setHeader("authorization", tokenStr);

        //返回给前端的结果中，tokenList设置为空
        userInfo.setTokenList(null);
        return userInfo;
    }

    private void checkHasCurrentPlatformLoginAuth(List<AccountWorkbenchListVo> workbenchList, String currentPlatformWorkbenchCode) {
        boolean hasCurrentPlatformLoginAuth = false;
        for (AccountWorkbenchListVo item : workbenchList) {
            if (currentPlatformWorkbenchCode.equals(item.getWorkbenchCode())) {
                hasCurrentPlatformLoginAuth = true;
                break;
            }
        }
        if (!hasCurrentPlatformLoginAuth) {
            // 没有权限登录当前平台
            throw new MemberException(MemberException.Type.MEM_0005);
        }
    }

    /**
     * 清除无效的token
     */
    private List<String> cleanToken(UserInfo userInfo) {
        List<String> cleanedTokenList = userInfo.getTokenList().stream().filter(item ->
                StrUtil.isNotBlank(RedisUtil.get(AuthConstants.REDIS_KEY_USER_TOKEN + item)))
                .collect(Collectors.toList());
        userInfo.setTokenList(cleanedTokenList);
        RedisUtil.set(AuthConstants.REDIS_KEY_USER_INFO + userInfo.getAccountId(), JSON.toJSONString(userInfo), SEVEN_DAY_MILLS);
        return userInfo.getTokenList();
    }
}
